/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#include "memorynode.h"
#include "validator.h"
#include "memory_node_yaml.h"
#include "validatornode.h"
#include "attributecontainervalidator.h"
#include <fstream>
#include <iostream>

MemoryNode::MemoryNode(const std::string &type):
    _type(type),
    children(),
    attribute_container(std::make_unique<AttributeContainer>("")),
    validator(),
    parent(nullptr),
    savable(true),
    movable(true),
    removable(true),
    duplicatable(true),
    _py_validation_result(true)
{
    attribute_container->setParent(this);
}

MemoryNode::MemoryNode(const MemoryNode &rhs):
    _type(rhs._type),
    children(),
    validator(rhs.validator),
    parent(rhs.parent),
    savable(rhs.savable),
    movable(rhs.movable),
    removable(rhs.removable),
    duplicatable(rhs.duplicatable),
    editable(rhs.editable),
    _py_validation_result(true)
{
    // do a deep copy here
    attribute_container.reset(new AttributeContainer(*rhs.attribute_container));
    // copy children
    for(const auto& child : rhs.children)
    {
        children.push_back(std::make_unique<MemoryNode>(*child));
    }
    updateParent();
}

MemoryNode &MemoryNode::operator=(const MemoryNode &rhs)
{
    // do a deep copy here
    _type = rhs._type;
    children.clear();
    attribute_container.reset(new AttributeContainer(*rhs.attribute_container));
    validator = rhs.validator;
    parent = rhs.parent;
    savable = rhs.savable;
    movable = rhs.movable;
    removable = rhs.removable;
    duplicatable = rhs.duplicatable;
    editable = rhs.editable;
    _py_validation_result = true;

    // copy children
    for(const auto& child : rhs.children)
    {
        children.push_back(std::make_unique<MemoryNode>(*child));
    }
    updateParent();
    return *this;
}

MemoryNode &MemoryNode::operator=(MemoryNode &&rhs)
{
    _type = std::move(rhs._type);
    children = std::move(rhs.children);
    attribute_container = std::move(rhs.attribute_container);
    validator = std::move(rhs.validator);
    parent = std::exchange(rhs.parent, nullptr);
    savable = std::move(rhs.savable);
    movable = std::move(rhs.movable);
    removable = std::move(rhs.removable);
    duplicatable = std::move(rhs.duplicatable);
    editable = rhs.editable;
    _py_validation_result = true;
    updateParent();
    return *this;
}

MemoryNode::MemoryNode(MemoryNode && rhs):
    _type(std::move(rhs._type)),
    children(std::move(rhs.children)),
    attribute_container(std::move(rhs.attribute_container)),
    validator(std::move(rhs.validator)),
    parent(std::exchange(rhs.parent, nullptr)),
    savable(std::move(rhs.savable)),
    movable(std::move(rhs.movable)),
    removable(std::move(rhs.removable)),
    duplicatable(std::move(rhs.duplicatable)),
    editable(rhs.editable),
    _py_validation_result(true)
{
    updateParent();
}

bool MemoryNode::operator==(const MemoryNode &rhs) const
{
    return _type == rhs._type &&
            children == rhs.children &&
            attribute_container == rhs.attribute_container &&
            validator == rhs.validator &&
            parent == rhs.parent &&
            savable == rhs.savable &&
            movable == rhs.movable &&
            duplicatable == rhs.duplicatable &&
            editable == rhs.editable;
}

void MemoryNode::addChild(std::unique_ptr<MemoryNode>&&child)
{
    child->parent = this;
    children.push_back(std::move(child));
}

void MemoryNode::addChild(std::unique_ptr<MemoryNode> &&child, const int &position)
{
    const auto iter_position = children.begin() + position;
    if(iter_position > children.end())
            return;
    child->parent = this;
    // add validator only if parent has validator
    if(validator)
    {
        ValidatorNode* child_validator = validator->getChild(child->getType());
        // only if parent has validator and child doesn't
        if(child_validator && !child->validator)
            child->validator = child_validator;
    }
    children.insert(iter_position, std::move(child));
}

void MemoryNode::addChild(MemoryNode *child)
{
    addChild(std::make_unique<MemoryNode>(*child));
}

void MemoryNode::addChild(MemoryNode *child, const int &position)
{
    addChild(std::make_unique<MemoryNode>(*child), position);
}

std::unique_ptr<MemoryNode> MemoryNode::removeChild(MemoryNode* child)
{
    const auto el_pos = std::find_if(children.begin(), children.end(), [child](auto& i){return child == i.get(); });
    if(el_pos == children.end())
        return nullptr;
    std::unique_ptr<MemoryNode> node = std::unique_ptr<MemoryNode>(el_pos->release());
    children.erase(el_pos);
    return node;
}

std::unique_ptr<MemoryNode> MemoryNode::removeChild(const int &position)
{
    const auto iter_position = children.begin() + position;
    if(iter_position >= children.end())
        return nullptr;
    std::unique_ptr<MemoryNode> node = std::unique_ptr<MemoryNode>(iter_position->release());
    children.erase(iter_position);
    return node;
}

void MemoryNode::removeChildren()
{
    children.clear();
}

bool MemoryNode::moveChildren(int sourcePosition, int count, MemoryNode *targetParent, int destPosition)
{
    std::list<std::unique_ptr<MemoryNode>> nodes;
    if(this == targetParent && destPosition > sourcePosition)
        destPosition -= count;

    const auto& src_begin = children.begin() + sourcePosition;
    const auto& src_end = src_begin + count;

    for(auto it = src_begin; it != src_end; ++it)
    {
        MemoryNode* node = it->get();
        nodes.emplace_back(it->release());
        node->parent = targetParent;
    }
    children.erase(src_begin, src_end);
    const auto& dest_it = targetParent->children.begin() + destPosition;
    targetParent->children.insert(dest_it, std::make_move_iterator(nodes.begin()), std::make_move_iterator(nodes.end()));
    return true;
}

int MemoryNode::index() const
{
    if(parent != nullptr)
    {
        const auto i = std::find_if(parent->children.begin(), parent->children.end(), [this](auto& i){return this == i.get(); });
        if (i != parent->children.end())
        {
            return static_cast<int>(std::distance(parent->children.begin(), i));
        }
    }
    return 0;
}

bool MemoryNode::isValid(const bool recursive) const
{
    return validator->isValid(this, recursive) && _py_validation_result;
}

bool MemoryNode::validate(const bool recursive)
{
    _py_validation_result = validator->runPythonValidator(this);
    return validator->validate(this, recursive) && _py_validation_result;
}

const std::string MemoryNode::getName() const
{
    if(attribute_container->hasAttribute("name"))
        return (*attribute_container->findAttribute("name"))->getValue();
    return std::string("NO_NAME ") + "(" + getType() + ")";
}

const std::list<std::string> MemoryNode::typeLocation() const
{
    std::list<std::string> location;
    location.push_front(getType());
    MemoryNode* node_parent = parent;
    if(!parent)
        return location;
    while(node_parent->parent)
    {
        location.push_front(node_parent->getType());
        node_parent = node_parent->parent;
    }
    return location;
}

const std::list<std::string> MemoryNode::nodeLocation() const
{
    std::list<std::string> location;
    location.push_front(getName());
    MemoryNode* node_parent = parent;
    if(!parent)
        return location;
    while(node_parent->parent)
    {
        location.push_front(node_parent->getName());
        node_parent = node_parent->parent;
    }
    return location;
}

MemoryNode *MemoryNode::getRoot() const
{
    MemoryNode* root = this->parent;
    if(!root)
        return const_cast<MemoryNode*>(this);
    while(root->parent)
    {
        root = root->parent;
    }
    return root;
}

std::vector<MemoryNode *> MemoryNode::getAllChildren() const
{
    std::vector<MemoryNode*> result;
    for(const auto& child: children)
    {
        result.push_back(child.get());
        std::vector<MemoryNode*> child_results = child->getAllChildren();
        std::move(child_results.begin(), child_results.end(), std::back_inserter(result));
    }
    return result;
}

MemoryNode *MemoryNode::getChild(const std::string &type) const
{
    for(const auto& child: children)
    {
        if(child->getType() == type)
            return child.get();
    }
    return nullptr;
}

MemoryNode *MemoryNode::getChild(const std::vector<std::string> &type) const
{
    if(type.size() == 1)
        return getChild(type[0]);
    else if(type.size() > 1)
    {
        MemoryNode * child = getChild(type[0]);
        if(child)
            return child->getChild(std::vector<std::string>(type.begin()+1, type.end()));
    }
    return nullptr;
}

bool MemoryNode::hasChild(const std::string &type) const
{
    return getChild(type) != nullptr;
}

bool MemoryNode::hasChild(const std::vector<std::string> &type) const
{
    return getChild(type) != nullptr;
}

int MemoryNode::childCount() const
{
    return static_cast<int>(children.size());
}

void MemoryNode::bind(MemoryNode* root_node, ValidatorNode* validator_node)
{
    root_node->validator = validator_node->getChild(root_node->getType());

    for(auto& node: root_node->children)
    {
        bind(node.get(), root_node->validator);
    }
}

std::pair<std::unique_ptr<MemoryNode>, std::vector<std::string>> MemoryNode::fromFile(const std::string &filename, const YAML::Node& schema, ValidatorNode* validator)
{
    // YAML library does not differentiate between a file that does not exist and a file that
    // exists, but its YAML structure is bad, or permissions are missing.
    std::ifstream fpath(filename);
    if (!fpath.good())
        throw std::runtime_error("File '" + filename + "' does not exist!");

    std::unique_ptr<MemoryNode> root_node(std::make_unique<MemoryNode>());
    SchemaValidator v;
    YAML::Node node = YAML::LoadFile(filename.c_str());
    const bool result = v.validate(schema, node);
    *root_node = node.as<MemoryNode>();

    if (!v.errors.empty() || !result)
    {
        for(const auto& msg : v.errors)
            std::cout << msg << std::endl;

        throw std::runtime_error("Failed to load map " + filename + ". Not valid!");
    }

    bind(root_node.get(), validator);
    return std::make_pair(std::move(root_node), v.errors);
}

void MemoryNode::save(const MemoryNode *root_node, const std::string &filename)
{
    const YAML::Node node(*root_node);
    YAML::Emitter out;
    out.SetBoolFormat(YAML::EMITTER_MANIP::TrueFalseBool);
    out << node;
    const std::string& yaml_text = out.c_str();
    const std::string result = std::regex_replace(yaml_text, std::regex("\\n"), "\r\n");
    std::ofstream yaml_file;
    yaml_file.open(filename, std::ios::binary);
    if(yaml_file.is_open())
    {
        yaml_file << result;
        yaml_file.close();
    }
    else
        throw std::runtime_error("Could not open file for writing: " + filename);
}

void MemoryNode::save(const std::string &filename) const
{
    save(this, filename);
}

bool MemoryNode::isAttributeSequence() const
{
    return validator->isAttributeSequence();
}

bool MemoryNode::isChildrenSequence() const
{
    return validator->isChildrenSequence();
}

bool MemoryNode::isSpecialSequence() const
{
    return validator->isSpecialSequence();
}

void MemoryNode::setPythonValidationResut(const bool result)
{
    _py_validation_result = result;
}

bool MemoryNode::pythonValidationResult() const
{
    return _py_validation_result;
}

bool MemoryNode::hasPythonValidator() const
{
    return validator->hasPythonValidator();
}

const std::vector<std::pair<Attribute*, std::vector<INodeRuleValidator*>>> MemoryNode::getFailedValidators() const
{
    return attribute_container->getFailedValidators();
}

std::vector<AttributeValidator*> MemoryNode::getMissingAttributes() const
{
    return validator->getAttributes()->missingAttributes(attribute_container.get());
}

const std::string &MemoryNode::getType() const
{
    return _type;
}

void MemoryNode::setType(const std::string &type)
{
    _type = type;
}

AttributeContainer *MemoryNode::getAttributeContainer() const
{
    return attribute_container.get();
}

const std::deque<std::unique_ptr<MemoryNode> > &MemoryNode::getChildren() const
{
    return children;
}

ValidatorNode *MemoryNode::getValidator() const
{
    return validator;
}

void MemoryNode::setValidator(ValidatorNode *new_validator)
{
    validator = new_validator;
}

MemoryNode *MemoryNode::getParent() const
{
    return parent;
}

void MemoryNode::setParent(MemoryNode *new_parent)
{
    parent = new_parent;
}

bool MemoryNode::isSavable() const
{
    return savable;
}

void MemoryNode::setSavable(bool is_savable)
{
    savable = is_savable;
}

bool MemoryNode::isEditable() const
{
    if(!editable)
        return false;
    if(parent)
    {
        const MemoryNode* parent_node = parent;
        while(parent_node)
        {
            if(!parent_node->isEditable())
                return false;
            parent_node = parent_node->parent;
        }
    }
    return editable;
}

void MemoryNode::setEditable(bool is_editable)
{
    editable = is_editable;
}

bool MemoryNode::isDeprecated() const
{
    return getValidator()->isDeprecated();
}

bool MemoryNode::hasDeprecated() const
{
    if(isDeprecated())
        return true;
    if(attribute_container->hasDeprecated())
        return true;

    for (auto& child: children)
    {
        if(child->hasDeprecated())
            return true;
    }
    return false;
}

std::vector<AttributeContainer *> MemoryNode::getDeprecatedAttributeContainers() const
{
    return attribute_container->getDeprecatedAttributeContainers();
}

std::vector<Attribute *> MemoryNode::getDeprecatedAttributes() const
{
    return attribute_container->getDeprecatedAttributes();
}

void MemoryNode::updateParent()
{
    for (auto& child: children)
    {
        child->parent = this;
    }
    attribute_container->setParent(this);
}

std::ostream& operator<<(std::ostream& stream, const MemoryNode& memory_node)
{
    stream << memory_node.getType() << ": " << std::endl;
    stream << *memory_node.getAttributeContainer();
    stream << std::endl;
    for (auto&kv: memory_node.getChildren())
    {
        stream << *kv << std::endl;
    }
    return stream;
}


